/**
 * 
 */
package SVGTriangulate;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import br.odb.libsvg.ColoredPolygon;
import br.odb.libsvg.SVGUtils;

/**
 * 
 * @author Daniel "Monty" Monteiro
 */
public class SVGTriangulateApp {
	// -----------------------------------------------------------------------------
	/**
	 * 
	 */
	public static final String PARAM_TOKEN = "--";
	/**
	 * 
	 */
	public static final String INPUT_TOKEN = "input ";
	/**
	 * 
	 */
	public static final String OUTPUT_TOKEN = "output ";

	// -----------------------------------------------------------------------------
	/**
	 * 
	 * @param out
	 */
	public static void printShortPreambule(OutputStream out) {
		try {
			out.write("SVG Triangulator - 2011 - Daniel Monteiro ( DanielMonteiro@id.uff.br )\n"
					.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// -----------------------------------------------------------------------------
	/**
	 * 
	 * @param out
	 */
	public static void printPreambule(OutputStream out) {
		try {
			out.write("Decomposes the shapes made of straight paths into simpler triangles.\n"
					.getBytes());
			out.write("3-Clause BSD license applies to the use and inspection of the source code of this applicaton.\n"
					.getBytes());
			out.write("USAGE: [JVM call procedure] SVGTriangulate --input file.svg --output file2.svg\n"
					.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// -----------------------------------------------------------------------------
	/**
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
//		String[] args = new String[ 4 ];
//		args[ 0 ] = "--input";
//		args[ 1 ] = "/home/monty/toomuch.svg";
//		args[ 2 ] = "--output";
//		args[ 3 ] = "/home/monty/out.svg";

		ArrayList<ColoredPolygon> original;
		ArrayList<ColoredPolygon> processed;

		String arg0 = null;
		String arg1 = null;
		String pathInput = null;
		String pathOutput = null;

		// <parameter processing>
		printShortPreambule(System.out);

		//------------------------		
		 if ( args.length != 4 ) {
			 printPreambule( System.out );
			 return;
		 }
		
		 arg0 = args[ 1 ];
		 arg1 = args[ 3 ];
		// ------------------------
		pathInput = arg0;
		pathOutput = arg1;

		// </parameter processing>
		// <reading the input file>
		System.out.println("Reading file:" + pathInput);
		original = readSVG(pathInput);

		//DEBUG
		writeSVG( original, pathOutput + ".original.svg" );

		// </reading the input file>
		// <processing geometry>
		System.out.println("Processing geometry:");
		processed = splitIntoMonotones(original);
		// processed = original;
		// </processing geometry>
		// <writing the output file>
	
		
		if ( args[ 2 ].equals( "--binary" ) ) {
			System.out.println("Writing binary file:" + pathOutput);
			writeBinarySVG(processed, pathOutput);
		} else if ( args[ 2 ].equals( "--output" ) ) {
			System.out.println("Writing file:" + pathOutput);
			writeSVG(processed, pathOutput);	
		}
		// ...
		System.out.println("Done!");
		// </writing the output file>
//		ArrayList<ColoredPolygon> piggyBacked = new ArrayList<ColoredPolygon>();
//		ColoredPolygon shape1 = new ColoredPolygon();
//		shape1.addPoint( 0 , 0 );
//		shape1.addPoint( 800 , 0 );
//		shape1.addPoint( 0 , 480 );
//		
//		shape1.color.set( 255, 128, 64, 32 );
//		
//		piggyBacked.add( shape1 );
//		writeSVG( piggyBacked, "/home/monty/piggyBack.svg" );
	}

	private static void writeBinarySVG(ArrayList<ColoredPolygon> processed,
			String pathOutput) {
		FileOutputStream os = null;
		DataOutputStream dos = null;
		try {
			os = new FileOutputStream(pathOutput);
			dos = new DataOutputStream( os );
			ArrayList<ColoredPolygon> pols = processed;
			ColoredPolygon pol;

			

			int size = pols.size(); 
//			os.write( size );
			dos.writeInt( size );
			for (int c = 0; c < size; ++c) {
				pol = pols.get(c);
				pol.writePath( os );
			}
			System.out.println( "wrote " + size + " polygons" );

			os.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			try {
				if ( os != null )
					os.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	// -----------------------------------------------------------------------------
	/**
	 * 
	 * @param processed
	 * @param pathOutput
	 */
	private static void writeSVG(ArrayList<ColoredPolygon> processed, String pathOutput) {
		FileOutputStream os = null;
		try {
			ArrayList<ColoredPolygon> pols = processed;
			ColoredPolygon pol;

			os = new FileOutputStream(pathOutput);
			String head;
			// TODO: change those page size values;
			head = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n<svg width='800' height='480'>\n<g>\n";
			os.write(head.getBytes());
			for (int c = 0; c < pols.size(); ++c) {
				pol = pols.get(c);
				pol.buildStyleProperty();
				head = pol.getSVGString();
				os.write(head.getBytes());
			}

			head = "</g>\n</svg>";
			os.write(head.getBytes());

			os.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			try {
				if ( os != null )
					os.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	// -----------------------------------------------------------------------------
	/**
	 * 
	 * @param proc4
	 * @return
	 */
	private static ArrayList<ColoredPolygon> triangulate(ColoredPolygon proc2) {
		ArrayList<ColoredPolygon> toReturn = new ArrayList<ColoredPolygon>();
		ColoredPolygon pol;
		for (int c = 0; c < proc2.npoints - 2; ++c) {
			pol = new ColoredPolygon();
			pol.color = proc2.color;
			pol.originalStyle = proc2.originalStyle;
			toReturn.add(pol);
			pol.addPoint(proc2.xpoints[0], proc2.ypoints[0]);
			pol.addPoint(proc2.xpoints[c + 1], proc2.ypoints[c + 1]);
			pol.addPoint(proc2.xpoints[c + 2], proc2.ypoints[c + 2]);
		}

		return toReturn;
	}

	// -----------------------------------------------------------------------------
	/**
	 * 
	 * @param monotonic
	 * @return
	 */
	private static ArrayList<ColoredPolygon> splitIntoMonotones(
			ArrayList<ColoredPolygon> polytonics) {

		ArrayList<ColoredPolygon> toReturn = new ArrayList<ColoredPolygon>();
		ArrayList<ColoredPolygon> pols = polytonics;
		int[] xi;
		float[] xv;
		ColoredPolygon proc;
		ColoredPolygon proc1;
		ColoredPolygon proc2;
		ArrayList<ColoredPolygon> proc3;
		ColoredPolygon proc4;
		ArrayList<ColoredPolygon> proc5;
		ColoredPolygon proc6;
		int c;
		int f;
		int bigger = 0;
		
		for (int d = 0; d < pols.size(); ++d) {
			
			proc = pols.get(d);
			
			if ( proc.npoints == 0 ) {
				System.out.println( "*WARNING*: image contains degenerated polygon!" );
				continue;
			}
			
			if ( proc.npoints > bigger )
				bigger = proc.npoints;
			
			if ( proc.npoints >= 35 ) {
				System.out.println( "*WARNING*: as of this beta version, the number of verteces you're pushing is not supported" );
			}
			xv = new float[proc.npoints];
			xi = new int[proc.npoints];

			for (int e = 0; e < proc.npoints; e++) {
				xv[e] = proc.xpoints[e];
				xi[e] = e;
			}

			SVGUtils.order(xv, xi);

			for (int h = 0; h < proc.npoints; h++) {
				c = xi[h];
				f = xi[(h + 1) % proc.npoints];

				proc1 = SVGUtils.CullXNoLessThan(proc.xpoints[c], proc);
				proc2 = SVGUtils.CullXNoMoreThan(proc.xpoints[f], proc1);
				proc3 = decomposeMonotone(proc2);

				for (int i = 0; i < proc3.size(); ++i) {
					proc4 = proc3.get(i);

					proc5 = triangulate(proc4);

					for (int j = 0; j < proc5.size(); ++j) {
						proc6 = proc5.get(j);
						toReturn.add(proc6);
					}
				}
			}
		}

		System.out.println("bigger polygon has " + bigger );
		
		return toReturn;
	}

	// -----------------------------------------------------------------------------
	/**
	 * 
	 * @param proc2
	 * @return
	 */
	private static ArrayList<ColoredPolygon> decomposeMonotone(
			ColoredPolygon proc) {
		ColoredPolygon polygon = proc;
		ColoredPolygon p;
		float v1x;
		float v1y;
		float v2x;
		float v2y;
		float v3x;
		float v3y;
		float vx;
		float vy;
		int c = 0;
		ArrayList<ColoredPolygon> toReturn = new ArrayList<ColoredPolygon>();

		while (polygon.npoints > 4) {
			p = new ColoredPolygon();
			p.color = polygon.color;
			p.originalStyle = polygon.originalStyle;
			c = 0;
			v1x = polygon.xpoints[0];
			v1y = polygon.ypoints[0];

			vx = v1x;
			vy = v1y;

			while (c < polygon.npoints) {

				vx = polygon.xpoints[c];
				vy = polygon.ypoints[c];

				if (vx != v1x) {
					break;
				}

				v1x = vx;
				v1y = vy;

				c++;
			}

			p.addPoint(v1x, v1y);

			while (c < polygon.npoints) {

				vx = polygon.xpoints[c];
				vy = polygon.ypoints[c];

				if (vx != v1x) {
					break;
				}
				c++;
			}

			v2x = vx;
			v2y = vy;
			v3x = vx;
			v3y = vy;
			p.addPoint(v2x, v2y);

			while (c < polygon.npoints) {

				vx = polygon.xpoints[c];
				vy = polygon.ypoints[c];

				if (vx != v2x) {
					break;
				}

				v3x = vx;
				v3y = vy;

				c++;
			}

			p.addPoint(v3x, v3y);
			p.addPoint(vx, vy);

			toReturn.add(p);

			p = new ColoredPolygon();
			p.color = polygon.color;
			p.originalStyle = polygon.originalStyle;
			while (c < polygon.npoints) {
				vx = polygon.xpoints[c];
				vy = polygon.ypoints[c];
				p.addPoint(vx, vy);
				c++;
			}

			polygon = p;
		}

		toReturn.add(polygon);

		return toReturn;
	}

	// -----------------------------------------------------------------------------
	/**
	 * 
	 */
	private static ArrayList<ColoredPolygon> readSVG(String pathInput) {
		ArrayList<ColoredPolygon> instance = new ArrayList<ColoredPolygon>();

		try {
			File file = new File(pathInput);
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(file);
			doc.getDocumentElement().normalize();
			br.odb.libscene.Color color;
			ColoredPolygon p = null;
			NodeList nodeLst;

			nodeLst = doc.getElementsByTagName("*");

			for (int s = 0; s < nodeLst.getLength(); s++) {
				Node fstNode = nodeLst.item(s);
				if (fstNode.getNodeType() == Node.ELEMENT_NODE
						&& (fstNode.getNodeName().equalsIgnoreCase("path") || fstNode
								.getNodeName().equalsIgnoreCase("rect"))) {
					float x = 0;
					float y = 0;
					float width = 0;
					float height = 0;

					Element fstElmnt = (Element) fstNode;
					color = SVGUtils.parseColorFromStyle(fstElmnt
							.getAttribute("style"));

					if (fstNode.getNodeName().equalsIgnoreCase("path"))
						p = SVGUtils.parseD(fstElmnt.getAttribute("d"), 800,
								480);
					else if (fstNode.getNodeName().equalsIgnoreCase("rect")) {
						p = new ColoredPolygon();

						if (fstElmnt.getAttribute("x") != "")
							x = Float.parseFloat(fstElmnt.getAttribute("x"));

						if (fstElmnt.getAttribute("y") != "")
							y = Float.parseFloat(fstElmnt.getAttribute("y"));

						if (fstElmnt.getAttribute("width") != "")
							width = Float.parseFloat(fstElmnt
									.getAttribute("width"));

						if (fstElmnt.getAttribute("height") != "")
							height = Float.parseFloat(fstElmnt
									.getAttribute("height"));

						p.addPoint(x, y);
						p.addPoint(x + width, y);
						p.addPoint(x + width, y + height);
						p.addPoint(x, y + height);
					}
					
					if ( p != null ) {
						
						p.color = color;
						p.originalStyle = fstElmnt.getAttribute("style");
						instance.add(p);
					}
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return instance;
	}
}
// -----------------------------------------------------------------------------
